home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-12-15 | 34.3 KB | 1,177 lines | [TEXT/ALFA] |
- ## -*-Tcl-*-
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "package.tcl"
- # created: 2/8/97 {6:15:10 pm}
- # last update: 15/12/97 {2:27:05 pm}
- # Author: Vince Darley
- # E-mail: <darley@fas.harvard.edu>
- # mail: Division of Engineering and Applied Sciences, Harvard University
- # Oxford Street, Cambridge MA 02138, USA
- # www: <http://www.fas.harvard.edu/~darley/>
- #
- # Copyright (c) 1997 Vince Darley
- #
- # How to ensure packages are loaded in the correct order?
- # (some may require Vince's Additions). Here perhaps we could
- # just use a Tcl8-like-approach: introduce a 'package' command
- # and have stuff like 'package Name 1.0 script-to-load'.
- # Then a package can just do 'package require Othername' to ensure
- # it is loaded. I like this approach.
- #
- # How to initialise each package at startup? If we use the above
- # scheme, then the startup script is purely a sequence of
- # 'package require Name' commands. The file 'prefs.tcl' is then
- # purely for user-meddling. Packages do not need to store anything
- # there. Sounds good to me.
- #
- # How to uninstall things? One approach here is a
- # 'package uninstall Name' command. Nice packages would provide
- # this.
- #
- # We need a default behaviour too. Some packages require no
- # installation at all (except placing in a directory), others
- # require sourcing, others need to add something to a menu. How
- # much of this should be automated and how much is up to the
- # package author?
- #
- # ----
- #
- # The solution below is to imitate Tcl 8. There is a 'package'
- # mechanism. There exists a index::extension() array which gives for
- # each package the means to load it --- a procedure name or a
- # 'source file' command. The package index is compiled
- # automatically by recursively scanning all files in the
- # Packages directory for 'package name version do-this'
- # commands.
- #
- # There's also 'package names', 'package exists name', and an
- # important 'package require name version' which allows one
- # package to autoload another...
- #
- # Pros of this approach: many packages, which would otherwise
- # require an installation procedure, now can be just dropped
- # in to the packages directory and they're installed! (After
- # rebuilding the package index). This is because 'package'
- # can declare a snippet of code, an addition to a menu etc…
- # ----
- #
- # Thanks to Tom Fetherston for some improvements here.
- # ###################################################################
- ##
-
- namespace eval package {}
- namespace eval date {}
- namespace eval remote {}
-
- lunion package::loaded "Alpha"
-
- ##
- # -------------------------------------------------------------------------
- #
- # "alpha::findAllExtensions" --
- #
- # package require all extensions the user has activated
- # -------------------------------------------------------------------------
- ##
- proc alpha::findAllExtensions {} {
- global package::activate package::loaded modifiedVars
- if [info exists package::activate] {
- cache::delete packageMenu
- lappend modifiedVars package::activate
- }
- if {${package::loaded} != ""} {
- eval lappend package::activate ${package::loaded}
- set package::loaded ""
- foreach pkg ${package::activate} {
- package::checkRequire $pkg
- }
- }
- catch {unset package::activate}
- }
-
- proc package::addPrefsDialog {pkg} {
- global package::prefs alpha::noMenusYet
- lunion package::prefs $pkg
- if ![info exists alpha::noMenusYet] {
- # we were called after start-up; build the menu now
- menu::buildSome global
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "alpha::package" --
- #
- # Mimics the Tcl standard 'package' command for use with Alpha.
- # It does however have some differences.
- #
- # package require ?-exact? ?-extension -mode -menu? name version
- # package exists ?-extension -mode -menu? name version
- # package names ?-extension -mode -menu?
- # package uninstall name version
- # package vcompare v1 v2
- # package vsatisfies v1 v2
- # package versions ?-extension -mode -menu? name
- # package type name
- # package info name
- # package maintainer name version {name email web-page}
- #
- # Equivalent to alpha::mode alpha::menu and alpha::extension
- #
- # package mode ...
- # package menu ...
- # package extension ...
- #
- # For extensions only:
- #
- # package forget name version
- # -------------------------------------------------------------------------
- ##
- proc alpha::package {cmd args} {
- global index::extension
- switch -- $cmd {
- "require" {
- set info [package::getInfo exact]
- global alpha::rebuilding
- if {$info != ""} {
- if {!${alpha::rebuilding} && [set version [lindex $args 1]] != ""} {
- if [info exists exact] {
- if {[lindex $info 0] != $version} {
- error "requested exact $version, had [lindex $info 0]"
- }
- } elseif {![alpha::package vsatisfies [lindex $info 0] $version]} {
- error "requested $version, had [lindex $info 0]"
- }
- }
- if {$type == "extension"} {
- global package::loaded alpha::noMenusYet \
- errorCode errorInfo
- if ![lcontains package::loaded $name] {
- message "Loading extension '$name'…"
- lappend package::loaded $name
- if [catch {uplevel \#0 [lindex $info 1]} res] {
- set package::loaded [lremove ${package::loaded} $name]
- return -code error -errorcode $errorCode \
- -errorinfo $errorInfo $res
- }
- if ![info exists alpha::noMenusYet] {
- package::markMenu $name 1
- }
- }
- }
- return [lindex $info 0]
- }
- if {!${alpha::rebuilding}} {
- error "can't find package $name"
- }
- }
- "uninstall" {
- set name [lindex $args 0]
- if {[llength $args] > 2} {
- set version [lindex $args 1]
- global alpha::rebuilding
- if {${alpha::rebuilding}} {
- global rebuild_cmd_count index::uninstall pkg_file
- switch -- [set script [lindex $args 2]] {
- "this-file" {
- set script [list removeFile $pkg_file]
- }
- "this-directory" {
- set script [list rm -r [file dirname $pkg_file]]
- }
- }
- set index::uninstall($name) [list $version $pkg_file $script]
- set args [lrange $args 3 end]
- if [llength $args] {
- eval alpha::package [lindex $args 0] $name $version [lrange $args 1 end]
- return
- }
- if {[info exists rebuild_cmd_count] && [incr rebuild_cmd_count -1] == 0} {
- return -code 11
- }
- }
- } else {
- cache::read index::uninstall
- return [set index::uninstall($name)]
- }
- }
- "forget" {
- catch {unset index::extension($name)}
- }
- "exists" {
- if {[package::getInfo] != ""} {return 1} else {return 0}
- }
- "type" {
- if {[package::getInfo] != ""} {return $type}
- error "No such package"
- }
- "info" {
- if {[set info [package::getInfo]] != ""} {return [concat $type $info]}
- error "No such package"
- }
- "maintainer" -
- "disable" -
- "help" {
- set name [lindex $args 0]
- if {[llength $args] > 2} {
- global alpha::rebuilding
- if {${alpha::rebuilding}} {
- set version [lindex $args 1]
- global rebuild_cmd_count index::$cmd
- set data [lindex $args 2]
- set index::${cmd}($name) [list $version $data]
- set args [lrange $args 3 end]
- if [llength $args] {
- eval alpha::package [lindex $args 0] $name $version [lrange $args 1 end]
- return
- }
- if {[info exists rebuild_cmd_count] && [incr rebuild_cmd_count -1] == 0} {
- return -code 11
- }
- }
- } else {
- cache::read index::$cmd
- return [set index::${cmd}($name)]
- }
- }
- "versions" {
- set info [package::getInfo]
- if {$info != ""} {
- return [lindex $info 0]
- }
- error "No such package"
- }
- "vcompare" {
- set c [eval package::_versionCompare $args]
- if {$c > 0 || $c == -3} {
- return 1
- } elseif {$c == 0} {
- return 0
- } else {
- return -1
- }
- }
- "vsatisfies" {
- set c [eval package::_versionCompare $args]
- return [expr $c >= 0 ? 1 : 0]
- }
- "names" {
- set names ""
- package::getInfo
- foreach type $which {
- if [array exists index::${type}] {
- eval lappend names [array names index::${type}]
- }
- }
- return $names
- }
- "mode" -
- "menu" -
- "extension" {
- eval alpha::$cmd $args
- }
- default {
- error "Unknown option '$cmd' to 'package'"
- }
- }
- }
-
- proc package::getInfo {{flags ""}} {
- uplevel [list set flags $flags]
- uplevel {
- set name [lindex $args 0]
- if {[regexp -- {-([^-].*)} $name "" which]} {
- if {[lsearch $flags $which] != -1} {
- set $which 1
- set name [lindex $args 1]
- set args [lrange $args 1 end]
- return [package::getInfo $flags]
- }
- if {[lsearch {extension mode menu} $which] == -1} {
- error "No such flag -$which"
- }
- set name [lindex $args 1]
- set args [lrange $args 1 end]
- } else {
- set which {extension mode menu}
- }
- foreach type $which {
- if {$type != "extension"} {cache::read index::${type}}
- if [info exists index::${type}($name)] {
- return [set index::${type}($name)]
- }
- }
- return ""
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "package::_versionCompare" --
- #
- # This proc compares the two version numbers. It returns:
- #
- # 0 equal
- # 1 equal but beta/patch update
- # 2 equal but minor update
- # -1 beta/patch version older
- # -2 minor version older
- # -3 major version newer
- # -5 major version older
- #
- # i.e. >= 0 is basically ok, < 0 basically bad
- #
- # It works for beta, alpha, dev, fc and patch version numbers.
- # Any sequence of letters starting b,a,d,f,p are assumed to
- # represent the particular item.
- #
- # 2.4 > 1.5 > 1.4.3 > 1.4.3b2 > 1.4.3b1 > 1.4.3a75 > 1.4p1 > 1.4
- # -------------------------------------------------------------------------
- ##
- proc package::_versionCompare {v1 v2} {
- regsub -all -nocase {([a-z])[a-z]+} $v1 {\1} v1
- regsub -all -nocase {([a-z])[a-z]+} $v2 {\1} v2
- set v1 [split $v1 .p]
- set v2 [split $v2 .p]
- set i -1
- set ret 0
- set mult 2
- while 1 {
- incr i
- set sv1 [lindex $v1 0]
- set sv2 [lindex $v2 0]
- if {$sv1 == "" && $sv2 == ""} { break }
- if {$sv1 == ""} {
- set v1 [concat 8 0 $v1]
- set v2 [concat 9 $v2]
- continue
- } elseif {$sv2 == ""} {
- set v1 [concat 9 $v1]
- set v2 [concat 8 0 $v2]
- continue
- } elseif {[regexp -nocase {[a-z]} "$sv1$sv2"]} {
- # beta versions
- foreach v {sv1 sv2} {
- if [regexp -nocase {[a-z]} [set $v]] {
- # f = 8, b = 7, a = 6, d = 5
- regsub -nocase {([^a-z])f} [set $v] {\1 7 } $v
- regsub -nocase {([^a-z])b} [set $v] {\1 6 } $v
- regsub -nocase {([^a-z])a} [set $v] {\1 5 } $v
- regsub -nocase {([^a-z])d} [set $v] {\1 4 } $v
- } else {
- # release version = 8, so it is larger than any of the above
- append $v " 8"
- }
- }
- set v1 [eval lreplace [list $v1] 0 0 $sv1]
- set v2 [eval lreplace [list $v2] 0 0 $sv2]
- set mult 1
- continue
- }
- if {$sv1 < $sv2} { set ret -1 ; break }
- if {$sv1 > $sv2} { set ret 1 ; break }
- set v1 [lrange $v1 1 end]
- set v2 [lrange $v2 1 end]
- }
- if {$i == 0} {
- # major version, return 0, -3, -5
- return [expr $ret * (-4*$ret + 1)]
- } else {
- return [expr $mult *$ret]
- }
- }
-
- proc package::reqInstalledVersion {name exact? {reqvers ""}} {
- global index::extension
- # called from installer
- set msg " I suggest you abort the installation."
- if [info exists index::extension($name)] {
- if {[set exact?] == ""} {return}
- set av [alpha::package versions $name]
- if {[set exact?] == "-exact"} {
- if {[alpha::package versions $name] != $reqvers} {
- alertnote "The installed version $av of '$name' is incorrect. Exact version $reqvers was requested.$msg"
- }
- } else {
- set reqvers [set exact?]
- if {$reqvers != ""} {
- set c [package::_versionCompare $av $reqvers]
- if {$c < 0 && $c != -3} {
- alertnote "The installed version $av of '$name' is too old. Version $reqvers was requested.$msg"
- } elseif {$c == -3} {
- alertnote "The installed version $av of '$name' may not be backwards compatible with the requested version ($reqvers).$msg"
- }
- }
- }
- } else {
- alertnote "This package requires the prior installation of '$name'. It is not currently installed.$msg"
- }
- }
-
- proc package::install {name version {script ""}} {
- global index::extension
- set index::extension($name) [list $version $script]
- cache::add index-extension variable index::extension($name)
- }
-
- proc package::checkRequire {pkg} {
- if [catch {alpha::package require $pkg} error] {
- if [catch {alertnote "The '$pkg' package had an error starting up: $error"} ] {
- alertnote "The '$pkg' package had an error starting up"
- }
- }
- }
-
-
- proc package::menuProc {dmy pkg} {
- switch -- $pkg {
- " " {
- return
- }
- "autoloadingExtensions" {
- alertnote "Extensions which contain no startup code are just\
- collections of Tcl procedures which are autoloaded when\
- necessary. Activation/deactivation is not relevant for them."
- }
- "readHelpFileForExtension" {
- alertnote "Select one of the extensions in this menu,\
- while holding a modifier key, and I will try and find its\
- associated help file — sadly, some extensions have no help files."
- }
- "activateOrDeactivateExtension" {
- alertnote "Select one of the extensions in this menu\
- to activate or deactivate it. Active extensions are marked\
- with bullets. Deactivation will usually not take effect until\
- you have restarted Alpha."
- }
- "describeExtension" {
- alertnote "Select one of the extensions in this menu,\
- while holding 'shift', and I will display some information\
- about that package."
- }
- "describePackage" {
- set pkg [dialog::optionMenu "Describe which package?" \
- [lsort -ignore [alpha::package names]]]
- package::describe $pkg
- }
- "rebuildPackageIndex" {
- alpha::rebuildPackageIndices
- }
- "listPackages" {
- global::listPackages
- }
- "uninstallPackage" {
- package::uninstall
- }
- "installBugFixesFrom" {
- # this item isn't in the menu by default anymore.
- set f [getfile "Select a bug-fix file…"]
- procs::patchOriginalsFromFile $f 1
- }
- default {
- # workaround Alpha bugs (perhaps Mercutio MDEF bug?)
- set pkg [package::buggyAlphaMenu $pkg]
- if [package::helpOrDescribe $pkg] {
- return
- }
- package::toggle $pkg
- }
- }
- }
-
- proc package::makeMenu {} {
- global index::extension package::loaded
- set names [lsort -ignore [lremove [alpha::package names -extension] "Alpha"]]
-
- set extList {}
- set autoList [list \
- "<S[menu::itemWithIcon {describeExtension} 81]" \
- "<S<U[menu::itemWithIcon {readHelpFileForExtension} 81]" \
- "<S[menu::itemWithIcon {autoloadingExtensions} 81]"]
- foreach mi $names {
- if {[string trim [lindex [set index::extension($mi)] 1]] == ""} {
- lappend autoList $mi
- } else {
- lappend extList $mi
- }
- }
-
- set m [list "describePackage…" "uninstallPackage…" "listPackages" \
- "rebuildPackageIndex" \
- [list menu -n "autoloadingExtensions" -p package::menu $autoList] \
- "(-" \
- "<S[menu::itemWithIcon {describeExtension} 81]" \
- "<S<U[menu::itemWithIcon {readHelpFileForExtension} 81]" \
- "<S[menu::itemWithIcon {activateOrDeactivateExtension} 81]" "(-"]
- menu -n "packages" -p package::menuProc [concat $m $extList]
- foreach pkg ${package::loaded} {
- if {[markMenuItem "packages" $pkg 1] != ""} {
- # buggy menus
- markMenuItem "packages" [quote::Menuify $pkg] 1
- }
- }
- }
-
-
- proc package::queryWebForList {} {
- global defaultAlphaDownloadSite remote::site PREFS
- set sitename [dialog::variable defaultAlphaDownloadSite "Query which site?"]
- set nm ${PREFS}:_pkgtemp
- set siteurl [set remote::site($sitename)]
-
- catch {removeFile $nm}
- message "Fetching remote list…"
- set type [url::fetch $siteurl $nm]
- package::okGotTheList $sitename
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "package::okGotTheList" --
- #
- # Helper proc which we can also call if the listing was interrupted
- # half-way through.
- # -------------------------------------------------------------------------
- ##
- proc package::okGotTheList {{sitename ""}} {
- global defaultAlphaDownloadSite remote::site PREFS
- if {$sitename == ""} {
- set sitename [dialog::variable defaultAlphaDownloadSite "From which site did you get the list?"]
- }
- set type [lindex [url::parse [set remote::site($sitename)]] 0]
- set nm ${PREFS}:_pkgtemp
- if {![file exists $nm] || (![file writable $nm]) || (![file size $nm])} {
- alertnote "There was an error fetching the list.\r\rIf it's still being\
- downloaded, wait till that's done and then select 'Ok Got The List'\
- from the downloads menu."
- enableMenuItem -m alphaDownloads "Ok, Got The List" on
- error "Error fetching list of new packages"
- } else {
- enableMenuItem -m alphaDownloads "Ok, Got The List" off
- }
- set fd [open $nm "r"]
- catch {set lines [split [read $fd] "\n\r"]}
- close $fd
-
- if [catch [list remote::process${type}Listing $lines] listing] {
- alertnote "Error interpreting list of new packages"
- error "Error interpreting list of new packages"
- }
- message "Processing list…"
- remote::processList $sitename $listing
- message "Updated remote package information."
- }
-
- proc package::active {pkg {text ""}} {
- global package::loaded
- if {[lsearch -exact ${package::loaded} $pkg] != -1} {
- if {$text != ""} { return [lindex $text 0] } else {return 1 }
- } else {
- if {$text != ""} { return [lindex $text 1] } else {return 0 }
- }
- }
-
- proc package::_editSite {{name ""} {loc ""}} {
- if {$name == ""} {
- set title "Name of new archive site"
- set name "Ken's Alpha site"
- set loc "ftp://ftp.ken.com/pub/Alpha/"
- } else {
- set title "Archive site name"
- }
- set y 10
- set yb 105
- set res [eval dialog -w 420 -h 135 \
- [dialog::textedit $title $name 10 y 40] \
- [dialog::textedit "URL for site" $loc 10 y 40] \
- [dialog::okcancel 250 yb 0]]
- if [lindex $res 3] { error "Cancel" }
- # cancel was pressed
- return [lrange $res 0 1]
- }
-
-
- proc package::addIndex {args} {
- global index::extension pkg_file
- cache::read index::extension
- foreach f [concat $args] {
- set pkg_file $f
- message "scanning $f…"
- catch {source $f}
- }
- cache::create index-extension "variable" index::extension
- unset pkg_file
- }
-
- proc package::helpFile {pkg {pointer 0}} {
- # read help file instead
- global HOME
- if ![catch {alpha::package help $pkg} res] {
- if {[lindex [set help [lindex $res 1]] 0] == "file"} {
- if {$pointer} {
- return "Help for this package is located in \"[lindex $help 1]\""
- } else {
- edit -r -c ${HOME}:Help:[lindex $help 1]
- }
- } elseif {[string index $help 0] == "\["} {
- # evaluate help at toplevel in a silly way!
- # (how else do we do it? besides removing the '[]' of course ;-)
- if {$pointer} {
- return "You can read help for this package by holding 'shift'\
- when\ryou select its name in the menu."
- } else {
- uplevel \#0 switch -- $help {}
- }
- } else {
- if {$pointer} {
- return $help
- } else {
- new -n "* '$pkg' Help *"
- insertText "Help for package '$pkg', version\
- [alpha::package versions $pkg]\r"
- insertText $help
- winReadOnly
- }
- }
- return
- }
- if {!$pointer} {
- alertnote "Sorry, there isn't a help file for that package.\
- You should contact the package maintainer."
- }
- return
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "package::helpFilePresent" --
- #
- # Help files must be of the same name as the package (minus 'mode' or
- # 'menu'), but may have any combination of mode, menu, or help after
- # that name. Whitespace is irrelevant.
- # -------------------------------------------------------------------------
- ##
- proc package::helpFilePresent {args} {
- set res ""
- cache::read index::help
- foreach pkg $args {
- lappend res [info exists index::help($pkg)]
- }
- return $res
- }
-
- proc package::helpOrDescribe {pkg} {
- if [set mods [expr [getModifiers] & 0xfe]] {
- if {$mods & 34} {
- package::helpFile $pkg
- } else {
- package::describe $pkg
- }
- return 1
- }
- return 0
- }
-
- proc package::describe {pkg {return 0}} {
- set info [alpha::package info $pkg]
- set type [lindex $info 0]
- set msg "Package '$pkg', version [alpha::package versions $pkg] is a"
- switch -- $type {
- "extension" {
- append msg "n $type, and is [package::active $pkg {active inactive}]."
- }
- "mode" {
- append msg " $type; modes are always active."
- }
- "menu" {
- append msg " $type, and is "
- global globalMenus_curr
- if ![lcontains globalMenus_curr $pkg] {
- append msg "not "
- }
- append msg "in use."
- }
- }
- cache::read index::maintainer
- if [info exists index::maintainer($pkg)] {
- set p [lindex [set index::maintainer($pkg)] 1]
- append msg "\rMaintainer: [lindex $p 0], [lindex $p 1]\r"
- append msg [lindex $p 2]
- }
- if $return {
- return $msg
- }
- alertnote $msg
- }
-
- proc package::buggyAlphaMenu {pkg} {
- if [alpha::package exists $pkg] { return $pkg }
- set pkg [join $pkg ""]
- if [alpha::package exists $pkg] { return $pkg }
- set pkg "[string toupper [string index $pkg 0]][string range $pkg 1 end]"
- if [alpha::package exists $pkg] { return $pkg}
- set pkg "[string tolower [string index $pkg 0]][string range $pkg 1 end]"
- if [alpha::package exists $pkg] { return $pkg}
- alertnote "No known package '$pkg'"
- error ""
- }
-
- proc package::activate {pkg} {
- global index::extension
- if {[string trim [lindex [set index::extension($pkg)] 1]] == ""} {
- alertnote "That package only ever auto-loads when necessary\
- and hence can't be activated."
- return
- }
- if [catch {alpha::package require $pkg}] {
- alertnote "The '$pkg' package had problems starting up."
- error ""
- }
- }
-
- proc package::markMenu {name val} {
- if {[markMenuItem "packages" $name $val] != ""} {
- # buggy menus
- markMenuItem "packages" [quote::Menuify $name] $val
- }
- }
-
- proc package::deactivate {pkg} {
- global package::activate modifiedVars package::loaded index::extension
- if {[string trim [lindex [set index::extension($pkg)] 1]] == ""} {
- alertnote "That package only ever auto-loads when necessary\
- and hence can't be deactivated."
- return
- }
- if ![catch {alpha::package disable $pkg} script] {
- # has a disable script
- if [catch {uplevel #0 $script}] {
- alertnote "$pkg had a problem disabling itself"
- }
- }
- package::markMenu $pkg 0
- if [info exists package::activate] {
- set package::activate [lremove ${package::activate} $pkg]
- lappend modifiedVars package::activate
- }
- set package::loaded [lremove ${package::loaded} $pkg]
- message "That may only take effect after restarting Alpha."
- }
-
- proc package::toggle {pkg} {
- global package::loaded
- if [lcontains package::loaded $pkg] {
- # deactivate it
- package::deactivate $pkg
- } else {
- package::activate $pkg
- }
- }
-
- proc package::uninstall {} {
- cache::read index::uninstall
- if {[set pkgs [array names index::uninstall]] == ""} {
- alertnote "I don't know how to uninstall anything."
- return
- }
- set pkg [dialog::optionMenu "Permanently remove which package/mode/menu?" [lsort -ignore $pkgs]]
- if {![dialog::yesno "Are you absolutely sure you want to uninstall $pkg?"]} {
- return
- }
- global pkg_file
- set pkg_file [lindex [set index::uninstall($pkg)] 1]
- set script [lindex [set index::uninstall($pkg)] 2]
- if [regexp "rm -r .*" $script check] {
- if {![dialog::yesno "This uninstaller contains a recursive removal command '$check'. Do you want to do this?"]} {
- return
- }
- }
- if [catch "uplevel \#0 [list $script]"] {
- alertnote "The uninstaller had problems!"
- }
- if {[dialog::yesno "All indices must now be rebuilt.\rShall I do this for you?"]} {
- alpha::rebuildPackageIndices
- rebuildTclIndices
- auto_reset
- } else {
- alertnote "This will probably cause problems."
- }
- if {[dialog::yesno "It is recommended that you quit and restart Alpha. Quit now?"]} {
- quit
- }
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "date::isOlder" --
- #
- # {Aug 22 1996} {Mar 26 22:17}
- #
- # We assume the format is 'Month Day Year' or 'Month Day Time', where
- # a time is distinguished by the presence of a colon. Months have
- # to be the standard three letter abbreviation (seems ok for all
- # ftp and http servers I've come across)
- # -------------------------------------------------------------------------
- ##
- proc date::isOlder {a b} {
- if {$a == $b} { return 0 }
- regexp {(\w+)[ \t]+(\w+)[ \t]+([\w:]+)} $a "" am ad ay
- regexp {(\w+)[ \t]+(\w+)[ \t]+([\w:]+)} $b "" bm bd by
- # check year
- set thisy [lindex [lindex [mtime [now] abbrev] 0] 3]
- if {$ay == $thisy} { set ay "00:00" }
- if {$by == $thisy} { set by "00:00" }
- set a_ist [regexp : $ay]
- set b_ist [regexp : $by]
- if {!$a_ist && !$b_ist} {
- if { $ay < $by } { return 1 } elseif {$by < $ay} { return 0}
- }
- if {$a_ist && !$b_ist} { return 0 }
- if {!$a_ist && $b_ist} { return 1 }
- # both are a year or both are times and yrs equal
- set months {Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec}
- set am [lsearch $months $am]
- set bm [lsearch $months $bm]
- if {$am < $bm} { return 1 } elseif {$bm < $am} { return 0 }
- if {$ad < $bd} { return 1 } elseif {$bd < $ad} { return 0 }
- if {$a_ist && $b_ist} {
- regsub {:} $ay {.} ay
- regsub {:} $by {.} by
- if { $ay < $by } { return 1 } elseif {$by < $ay} { return 0}
- }
- # same !
- return 0
- }
-
-
- # ◊◊◊◊ Handle remote menu ◊◊◊◊ #
- proc remote::menuProc {menu item} {
- global remote::site modifiedArrVars defaultAlphaDownloadSite
- switch -- $item {
- "Update List From A Web Archive Site" {
- package::queryWebForList
- }
- "Ok, Got The List" {
- package::okGotTheList
- }
- "Add Web Or Ftp Archive Site" {
- array set remote::site [package::_editSite]
- lappend modifiedArrVars remote::site
- }
- "Edit Web Or Ftp Archive Site" {
- set sitename [dialog::optionMenu "Edit which site?" \
- [lsort -ignore [array names remote::site]]]
-
- array set remote::site \
- [package::_editSite $sitename [set remote::site($sitename)]]
- lappend modifiedArrVars remote::site
- }
- "Remove Web Or Ftp Archive Site" {
- set sitename [dialog::optionMenu "Remove which site?" \
- [lsort -ignore [array names remote::site]]]
- unset remote::site($sitename)
- lappend modifiedArrVars remote::site
- }
- "Describe Item" {
- alertnote "Select one of the packages, and I'll tell you\
- when it was last modified, and from where it would be downloaded."
- }
- "Ignore Item" {
- alertnote "'Ignoring' a package tells me to remove it from\
- new and updated package lists. It'll still be listed lower\
- down in the menu"
- }
- "Select Item To Download" {
- alertnote "Select one of the packages, and it will be\
- downloaded from its site on the internet, decompressed\
- and installed."
- }
- default {
- remote::get $item
- }
- }
-
- }
-
-
- proc remote::makeDownloadsMenu {} {
- global remote::listing
- set l [list "Update List From A Web Archive Site…" \
- "(Ok, Got The List" \
- "<E<SRemove Web Or Ftp Archive Site…" \
- "<S<BEdit Web Or Ftp Archive Site…" \
- "<SAdd Web Or Ftp Archive Site…" "(-" \
- "<S[menu::itemWithIcon {Describe Item} 81]" \
- "<S<U[menu::itemWithIcon {Ignore Item} 81]" \
- "<S[menu::itemWithIcon {Select Item To Download} 81]" ]
- foreach a ${remote::listing} {
- set type [lindex $a 1]
- regsub -all {\.(sit|bin|hqx)} [set name [lindex $a 2]] "" name
- lappend [lindex {other gone new uptodate update} [expr $type + 2]] $name
- if {$type == -1} {
- lappend disable $name
- }
- }
- if [info exists update] {
- lappend l "(-" "/\x1e(Updated items^[text::Ascii 79 1]"
- eval lappend l [lsort -ignore $update]
- }
- if [info exists new] {
- lappend l "(-" "/\x1e(New items^[text::Ascii 79 1]"
- eval lappend l [lsort -ignore $new]
- }
- if [info exists uptodate] {
- lappend l "(-" "(Current items"
- eval lappend l [lsort -ignore $uptodate]
- }
- if [info exists other] {
- lappend l "(-" "(Other items"
- eval lappend l [lsort -ignore $other]
- }
- if [info exists gone] {
- lappend l "(-" "(Vanished items"
- eval lappend l [lsort -ignore $gone]
- }
- menu -n "alphaDownloads" -m -p remote::menuProc $l
- if [info exists disable] {
- foreach a $disable {
- enableMenuItem "alphaDownloads" $a off
- }
- }
- }
-
- proc remote::processftpListing {lines} {
- set files {}
- foreach f [cdr [lreplace $lines end end]] {
- set nm [lindex $f end]
- if {[string length $nm]} {
- if {[string match "d*" $f]} {
- #lappend files "$nm/"
- } else {
- regexp {[A-Z].*$} [lreplace $f end end] time
- set date [lindex $time end]
- if {![regexp {^19[89][0-5]$} $date]} {
- # reject anything pre 1996
- lappend files [list $nm $time]
- }
- }
- }
- }
- return $files
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "remote::processhttpListing" --
- #
- # Extract all things like <A HREF="/~vince/pub/">Parent Directory</A>
- # followed by a date. Massage the date into 'Month day year'.
- #
- # I don't know if this will work for all http servers! It works for
- # mine.
- # -------------------------------------------------------------------------
- ##
- proc remote::processhttpListing {lines} {
- set files {}
- foreach f $lines {
- if [regexp {<A HREF="([^"]*)">.*</A>[ \t]*([^ \t]+)[ \t]} $f "" name date] {
- if ![regexp {/$} $name] {
- if {![regexp {[89][0-5]$} $date]} {
- # reject anything pre 1996
- set date [split $date -]
- set md "[lindex $date 1] [lindex $date 0] "
- append md [expr [lindex $date 2] < 80 ? 20 : 19]
- append md [lindex $date 2]
- lappend files [list $name $md]
- }
- }
- }
- }
- return $files
- }
-
- proc remote::versionOneNewer {one two} {
- return 1
- }
-
- proc remote::processList {sitename {l ""}} {
- global remote::listing modifiedVars
- # removed vanished items from the menu
- regsub -all {(\.|-)[0-9]+([a-zA-Z][0-9]+)?} [set ll $l] "" ll
- foreach i ${remote::listing} {
- if [string match "*${sitename}*" $i] {
- regsub -all {(\.|-)([0-9]+([a-zA-Z][0-9]+)?)} \
- [set ii [lindex $i 2]] "" ii
- if {[lsearch -glob $ll "$ii *"] == -1} {
- # it's vanished
- lappend removed $i
- lappend _removed [lindex $i 0]
- }
- }
- }
- if [info exists removed] {
- set remote::listing [lremove -l ${remote::listing} $removed]
- }
- # process new items
- foreach i $l {
- set namepart [lindex $i 0]
- set timepart [lindex $i 1]
- regsub -all {\.(sit|bin|hqx|tcl)} [set name $namepart] "" name
- regsub -all {(\.|-)[0-9]+([a-zA-Z][0-9]+)?} $name "" name
- if {[set idx [lsearch -glob ${remote::listing} "${name} *"]] != -1} {
- # update old item
- set item [lindex ${remote::listing} $idx]
- if {[lindex $item 2] != $namepart} {
- # it's changed
- set item [lreplace $item 1 end 2 $namepart $timepart $sitename]
- set remote::listing [lreplace ${remote::listing} $idx $idx $item]
- lappend _updated $name
- } elseif {[date::isOlder [lindex $item 3] $timepart]} {
- # date has changed
- set item [lreplace $item 1 end 2 $namepart $timepart $sitename]
- set remote::listing [lreplace ${remote::listing} $idx $idx $item]
- lappend _updated $name
- }
- } else {
- # new package
- lappend remote::listing [list $name 0 $namepart $timepart $sitename]
- lappend _new $name
- }
-
- }
- lappend modifiedVars remote::listing
- remote::makeDownloadsMenu
- package::makeMenu
- ensureset _updated "none"
- ensureset _new "none"
- ensureset _removed "none"
- if [catch {alertnote "Remote information, NEW: $_new, UPDATED: $_updated, REMOVED: ${_removed}."}] {
- alertnote "Remote information, [llength $_new] new, [llength $_updated] updated and [llength $_removed] packages removed."
- }
- }
- proc remote::updateDatabase {idx val} {
- global remote::listing
- set item [lindex ${remote::listing} $idx]
- if {[lindex $item 1] != $val} {
- # it's changed
- set item [lreplace $item 1 1 $val]
- set remote::listing [lreplace ${remote::listing} $idx $idx $item]
- }
- }
-
- proc remote::pkgIndex {name} {
- global remote::listing
- if {[set i [lsearch -glob ${remote::listing} "${name} *"]] == -1} {
- set i [lsearch -glob ${remote::listing} \
- "[string toupper [string index ${name} 0]][string range $name 1 end] *"]
- }
- return $i
- }
-
- proc remote::pkgDetails {name} {
- global remote::listing
- set idx [lsearch -glob ${remote::listing} "${name} *"]
- return [lindex ${remote::listing} $idx]
- }
-
- proc remote::get {pkg} {
- global remote::listing HOME remote::site downloadFolder
- # get pkg
- if {[set idx [remote::pkgIndex $pkg]] == -1} {
- alertnote "Sorry, I don't know from where to download that package."
- error ""
- }
- set item [lindex ${remote::listing} $idx]
-
- if [set mods [expr [getModifiers] & 0xfe]] {
- if {$mods & 34} {
- # just shift key demote the item in the hierarchy
- set itm [lindex $item 1]
- if {$itm == 0 || $itm == 2} { set itm 1 } else { set itm -2 }
- set item [lreplace $item 1 1 $itm]
- set remote::listing [lreplace ${remote::listing} $idx $idx $item]
- global modifiedVars
- lappend modifiedVars remote::listing
- remote::makeDownloadsMenu
- message "Package '$pkg' demoted."
- return
- } else {
- # describe the item
- alertnote "File '[lindex $item 2]', last modified [lindex $item 3], to be downloaded from [lindex $item 4], at [set remote::site([lindex $item 4])]"
- return
- }
- }
- set file [lindex $item 2]
- set sitename [lindex $item 4]
- # get the file
- if {![file exists $downloadFolder] || ![file isdirectory $downloadFolder]} {
- alertnote "Your Download Folder does not exists. I'll download to\
- Alpha's home directory."
- set downloadFolder $HOME
- }
- if [catch {url::fetchFrom [set remote::site($sitename)] ${downloadFolder}: $file} err] {
- alertnote "Fetch error '$err'"
- error ""
- }
- # update database
- remote::updateDatabase $idx 1
- remote::makeDownloadsMenu
- package::makeMenu
- # install
- set filepre [lindex [split $file .] 0]
- # unstuff (this may happen automatically)
- foreach ext {.hqx .bin .sit} {
- set stuffed [glob -nocomplain "${downloadFolder}:${filepre}*${ext}"]
- if {[llength $stuffed] == 1} {
- set ff [lindex $stuffed 0]
- message "Decompressing [file tail $ff]…"
- set name [file tail [app::launchFore SITx]]
- sendOpenEvent -r 'SITx' $ff
- }
- }
- # install
- set files [glob -nocomplain -t TEXT "${downloadFolder}:${filepre}*"]
- if {[llength $files] == 0} {
- # look for directory
- set dirs [glob -nocomplain "${downloadFolder}:${filepre}*:"]
- if {[llength $dirs] == 1} {
- set local [lindex $dirs 0]
- set files [glob -nocomplain -t TEXT "${local}*\[i|I\]{nstall,NSTALL}"]
- } else {
- set files ""
- set local $downloadFolder
- }
- }
- if {[llength $files] == 0} {
- alertnote "I can't find a suitable, unique install file. You must find it yourself."
- # open dir in finder
- openFolder $local
- switchTo Finder
- return
- }
- if {[llength $files] > 1} {
- set f [listpick -p "Which file is the installer?" $files]
- } else {
- set f [lindex $files 0]
- }
- edit $f
- global mode
- if {$mode != "Inst"} {
- alertnote "I don't know what to do with this package from here."
- } else {
- if {[dialog::yesno "You can install this extension from the install menu.\rShall I do that for you?"]} {
- install::installThisPackage
- }
- }
- }
-
-